<template>
  <div class="test-grid">
    <div
      id="panel"
      :style="{ height: bottomPosition + 'px' }"
      @drop="onInitTableDragEnd"
    >
      <div class="empty" v-show="!nodes.length">拖拽添加一张表</div>
      <div
        :class="{ node: 1, unuse: item.unuse }"
        v-for="(item, i) of nodes"
        :key="i"
        :style="{
          left: item.x + 'px',
          top: item.y + 'px',
          opacity: !item.unuse || showDragProcess ? 1 : 0,
        }"
        @dragenter="onTableDragEnter(item)"
        @dragover="onDragOver"
        @drop="onTableDragEnd"
      >
        {{ item.unuse ? "添加表关联" : item.name }}
        <div class="line1" v-if="item.target">
          <div class="left"></div>
          <div class="text" v-if="item.unuse"></div>
          <el-tooltip content="编辑关联关系" placement="left" v-else>
            <div
              class="icon"
              :class="[item.joinType]"
              @click="onEditJoin(item)"
            ></div>
          </el-tooltip>
          <div class="right"></div>
        </div>
        <div
          class="line2"
          v-if="item.target"
          :style="countLineStyle(item)"
        ></div>
      </div>
    </div>
    <div class="tables">
      <div
        class="item"
        v-for="(item, i) of tables"
        :key="i"
        draggable
        @dragstart="onTableDragStart(item)"
      >
        {{ item.name }}
      </div>
    </div>
    <el-drawer
      title="编辑表关联"
      :visible.sync="showEditJoin"
      direction="rtl"
      :show-close="false"
    >
      <el-form label-width="80px" size="mini">
        <el-form-item label="连接方式">
          <el-select v-model="joinForm.joinType">
            <el-option label="左关联" value="left-join"></el-option>
            <el-option label="右关联" value="right-join"></el-option>
            <el-option label="内关联" value="inner-join"></el-option>
            <el-option label="外关联" value="join"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="onConfirmJoin">确定</el-button>
          <el-button @click="showEditJoin = false">取消</el-button>
        </el-form-item>
      </el-form>
    </el-drawer>
  </div>
</template>
<script>
import { getRandomStr } from "../util";
export default {
  data() {
    return {
      tables: [
        { name: "A" },
        { name: "B" },
        { name: "C" },
        { name: "D" },
        { name: "E" },
        { name: "F" },
        { name: "G" },
        { name: "H" },
        { name: "I" },
      ],
      nodes: [],
      bottomPosition: 200,
      tree: null,
      selectedTable: null,
      selectedNode: null,
      showDragProcess: false,
      joinForm: {},
      showEditJoin: false,
      editJoinType: "add",
    };
  },
  created() {},
  mounted() {
    document.addEventListener("dragover", function (e) {
      e.preventDefault();
    });
    document.addEventListener("drop", () => {
      this.showDragProcess = false;
    });
  },
  methods: {
    onEditJoin(item) {
      this.editJoinType = "edit";
      this.joinForm = item;
      this.showEditJoin = true;
    },
    // 拖拽添加第一张表
    onInitTableDragEnd() {
      if (!this.nodes.length) {
        this.nodes.push({
          name: this.selectedTable.name,
          chidren: [],
          x: 0,
          y: 0,
          id: getRandomStr(5),
        });
        this.countTree();
      }
    },
    // 拖拽结束，添加关联关系
    onTableDragEnd() {
      if (!this.selectedNode.unuse) return;
      this.selectedNode = this.nodes.find(
        (i) => i.id == this.selectedNode.target
      );
      this.joinForm = { joinType: "left-join" };
      this.editJoinType = "add";
      this.showEditJoin = true;
    },
    // 确认编辑关联关系，分新增和修改两种情况处理
    onConfirmJoin() {
      if (this.editJoinType == "add") {
        this.onAddJoin();
      } else {
        // 修改关联关系时
        this.showEditJoin = false;
      }
    },
    // 添加新关联关系时
    onAddJoin() {
      let newNodes = this.nodes.filter((i) => !i.unuse);
      newNodes.forEach((i) => (i.chidren = []));
      newNodes.push({
        ...this.joinForm,
        name: this.selectedTable.name,
        target: this.selectedNode.id,
        x: 0,
        y: 0,
        chidren: [],
        id: getRandomStr(5),
      });
      this.nodes = newNodes;
      this.countTree();
      this.showEditJoin = false;
    },
    onDragOver(e) {
      e.preventDefault();
    },
    // 拖拽到想关联的节点时记录下来
    onTableDragEnter(node) {
      this.selectedNode = node;
    },
    // 拖拽开始，记录选择的表
    onTableDragStart(table) {
      this.selectedTable = table;
      this.showDragProcess = true;
    },
    // 计算左边连接线
    countLineStyle(item) {
      if (!item.target) return;
      let target = this.nodes.find((i) => i.id == item.target);
      if (target.y == item.y) {
        return { width: "20px", height: "1px" };
      } else {
        return { width: "1px", height: item.y - target.y + "px" };
      }
    },
    // 计算节点树形结构
    countTree() {
      this.nodes.forEach((item) => {
        let target = this.nodes.find((i) => i.id == item.target);
        target && target.chidren.push(item);
      });
      let unuseNodes = [];
      this.nodes.forEach((i) => {
        let unuseNode = {
          unuse: true,
          x: 0,
          y: 0,
          chidren: [],
          target: i.id,
        };
        i.chidren.push(unuseNode);
        unuseNodes.push(unuseNode);
      });
      this.nodes = this.nodes.concat(unuseNodes);
      this.tree = this.nodes.find((i) => !i.target);
      this.countNodePosition(0, 0, this.tree);
      this.bottomPosition = Math.max(
        this.nodes.reduce((r, i) => Math.max(r, i.y), 0) + 50,
        200
      );
    },
    // 计算节点位置
    countNodePosition(xLevel, yLevel, node) {
      node.x = xLevel * 270 + 20;
      node.y = yLevel * 40 + 20;
      let startYLevel = yLevel;
      if (node.chidren.length) {
        node.chidren.forEach((item) => {
          startYLevel =
            this.countNodePosition(xLevel + 1, startYLevel, item) + 1;
        });
        startYLevel--;
      }
      return startYLevel;
    },
  },
};
</script>
<style lang="scss">
.test-grid {
  #panel {
    position: relative;
    background-color: #f2f2f2;
    overflow-x: auto;
    .empty {
      position: absolute;
      left: 0;
      top: 0;
      bottom: 0;
      right: 0;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #999;
    }
    .node {
      width: 180px;
      height: 30px;
      position: absolute;
      border: 1px solid #fff;
      border-left: 2px solid skyblue;
      line-height: 30px;
      text-align: center;
      box-sizing: border-box;
      transition: opacity 0.2s;
      background-color: #fff;
      .line1 {
        position: absolute;
        width: 70px;
        height: 30px;
        left: -71px;
        top: -1px;
        display: flex;
        align-items: center;
        .left,
        .right {
          height: 1px;
          background-color: skyblue;
        }
        .left {
          width: 10px;
        }
        .right {
          width: 30px;
        }
        .icon {
          cursor: pointer;
          width: 30px;
          height: 16px;
          background-size: contain;
          background-position: center center;
          background-repeat: no-repeat;
          &.left-join {
            background-image: url("../assets/images/leftJoin.png");
          }
          &.right-join {
            background-image: url("../assets/images/rightJoin.png");
          }
          &.inner-join {
            background-image: url("../assets/images/innerJoin.png");
          }
          &.join {
            background-image: url("../assets/images/join.png");
          }
        }
      }
      .line2 {
        position: absolute;
        background-color: skyblue;
        right: 248px;
        bottom: 13px;
      }
      &.unuse {
        color: #aaa;
        font-size: 14px;
        border: 1px dashed #aaa;
        background-color: #f2f2f2;
        .line1 .left,
        .line1 .right,
        .line2 {
          background-color: #aaa;
        }
        .line1 .text {
          width: 30px;
          height: 1px;
          background-color: #aaa;
        }
      }
    }
  }
  .tables {
    width: 200px;
    padding: 0 10px 10px;
    border: 1px solid skyblue;
    margin-top: 20px;
    .item {
      border: 1px solid skyblue;
      line-height: 30px;
      text-align: center;
      margin-top: 10px;
    }
  }
}
</style>